iT邦幫忙

2025 iThome 鐵人賽

DAY 4
0
Software Development

新 .NET & Azure & IoT & AI 開源技術實戰手冊 (含深入官方程式碼講解) 系列 第 4

「單 cs run 模式」底層原理跟開源程式碼由來

  • 分享至 

  • xImage
  •  

「單 cs run 模式」底層原理跟開源程式碼由來

我在 GitHub .NET SDK GitHub 原始碼 : https://github.com/dotnet/sdk ,查到是由 jjonescz 提交的 commit 46ce5ca 開始。

他設計主要的邏輯

操作

  • 允許使用者直接執行單一 C# 原始檔:dotnet run hello.cs,不需要 .csproj 或建立傳統專案結構。
  • CLI 內部會動態生成一個「虛擬隱式專案檔」,基礎等同於 dotnet new console 預設模板,然後套用針對 file-based app 的特殊屬性:
    • FileBasedProgram=true:標記此為單檔模式
    • PublishAot=true
    • 關閉預設的 Compile Items,改為明確指定來源檔案集合
    • 輸出與中繼資料 obj/bin 改放置於使用者層級的暫存 / 快取目錄,他這邊目的是避免乾擾目前資料夾

檔案與 Entry Point 檢查

  • 使用者傳入的 .cs 會被檢測是否包含 top-level statements,這些語句會被轉換為隱式 Main
  • 目前 .NET 10 僅支援 top-level statements 作為進入點;如果檔案內自行宣告 static void Main 則不視為支援情境(規格刻意限制以降低複雜度)。
  • CLI 會掃描該單一來源檔並推斷 entry point;若未偵測到 top-level statements 則報錯。未實作多 entry-point 對應邏輯,因多檔案尚未全面支援。

多檔案支援狀態

  • .NET 10 僅支援「單一來源檔」。
  • 編譯時會設定 EnableDefaultCompileItems=false,只將使用者指定的那個檔案加入編譯。
  • 規劃預計 .NET 11+ 會加入 #import ./another-file.cs 或類似 directive 方式顯式納入額外檔案。
  • 目前透過此限制,能顯著簡化快取與重建判斷模型。

指令碼式(Script-like)專案 Metadata Directives

單檔模式允許在檔案頂部以「檔案層級指示(directives)」宣告專案設定,常見:

Directive 作用 轉換成 MSBuild 範例
#:sdk 指定 SDK <Project Sdk="Microsoft.NET.Sdk" />
#:property 設定屬性 <PropertyGroup><PublishAot>true</PublishAot></PropertyGroup>
#:package 加入 NuGet 套件 <ItemGroup><PackageReference Include="X" Version="Y" /></ItemGroup>
#:project 參考其他專案 <ItemGroup><ProjectReference Include="../lib/lib.csproj" /></ItemGroup>

CLI 會解析這些 directives,建立對應的記憶體內 MSBuild Project,然後再進入建置流程。

編譯與效能優化

為了降低「一次性小工具」的啟動成本,設計了多層快取與跳躍機制:

  1. 若只改動單一 .cs 檔內容,且 MSBuild 結構未變(無新增 directives、套件),可直接呼叫 Roslyn 編譯器(csc)跳過完整 MSBuild 評估 — 可透過 FileBasedProgramCanSkipMSBuild=false 停用。
  2. 若檔案完全無變動(時間戳與內容 Hash 比對通過),直接執行上次產生的輸出 EXE(或 AOT 產物)。
  3. Up-to-date check 利用:
    • 原始碼內容 Hash
    • 產物時間戳記與記錄檔(metadata file)
  4. 可用 --no-cache 強制重建;亦支援環境變數調整快取策略。

輸出與自動清理策略

  • 產物(含 obj/bin)存放於:%TEMP%/dotnet-file-based/<hash>(Windows 類似路徑;其他平台在使用者 cache/temp 目錄下)。
  • <hash> 以 entry 檔案的完整路徑 + 關鍵設定(如 TFM、RuntimeIdentifier)計算,確保不同來源不互相覆蓋。
  • 內建清理服務:
    • 預設每 2 天執行一次掃描
    • 移除 30 天未使用的產物
    • 可執行 dotnet clean-file-based-app-artifacts 立即清理
    • 設定 DOTNET_CLI_DISABLE_FILE_BASED_APP_ARTIFACTS_AUTOMATIC_CLEANUP=true 關閉自動清理

指令相容性與行為差異

單檔模式支援與傳統專案一致的大部分指令並套用特化預設:

  • dotnet run file.cs:主流程;內部等同「隱式還原 + 快取判斷 + 可能增量編譯 + 執行」。
  • dotnet build file.cs:產生(或更新)產物但不執行。
  • dotnet publish file.cs:支援包含 Native AOT 預設啟用(若環境條件成立)。
  • dotnet pack file.cs:會生成一個暫時性專案以封裝成 NuGet(使用者較少用,但為一致性保留)。
  • dotnet clean file.cs:清除該單檔對應 Hash 目錄。
  • dotnet restore file.cs:解析 directives 中的 package dependencies;若無套件則可能直接 no-op。

特殊差異:

  • 預設 TFM 可能鎖定最新 LTS / STS(視安裝 SDK 版本);
  • AOT / Trim 屬性預設可啟動(視目標平台 + 旗標);
  • 禁用複雜專案檔特性(多 TargetFrameworks、複數 ItemGroup pattern 等)。

底層流程簡化時序

dotnet run hello.cs
    └── 解析參數:檢測是 file-based 模式
                └── 建構 FileBasedAppContext(路徑、Hash、快取目錄)
                            ├── 讀取/解析 directives -> 建立 In-Memory Project
                            ├── 計算變更指紋 (hash of source + directives + packages)
                            ├── 快取命中?
                            │     ├── 是 -> 直接執行輸出
                            │     └── 否 -> (可能)走快路徑 csc 編譯或完整 MSBuild
                            └── 更新快取記錄 & 啟動執行

建議

  • 適合:小工具、教學示例、腳本。
  • 不適合:需要多專案結構、複雜打包、跨平台大量資源嵌入的應用。
  • 若日後成長:建議改為為正式 .csproj,保留 directives 內容遷移至專案檔 <PropertyGroup> / <ItemGroup>

上一篇
底層運行流程
下一篇
例子: 天氣小幫手
系列文
新 .NET & Azure & IoT & AI 開源技術實戰手冊 (含深入官方程式碼講解) 5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言